package com.airport.schedule.genetic;

import com.airport.schedule.Problem;
import com.airport.schedule.ScheduleEvaluator;
import com.airport.schedule.ScheduleSolution;
import com.airport.schedule.model.AdHocRule;
import com.airport.schedule.model.Flight;
import com.airport.schedule.model.FlightGate;

import java.util.*;

public class RawGeneticScheduler extends Thread {

    private Problem problem;
    private Genotype genotype;
    private int maxIterations;
    private ScheduleSolution solution;

    public RawGeneticScheduler(Problem problem, int popSize,
                                double muteRate, double crossRate,
                                int maxIterations) {
        this.problem = problem;
        this.maxIterations = maxIterations;
        int numGenes = problem.getScheduleFlights().size();
        int[][] valids = new int[numGenes][];
        for (int i = 0; i < numGenes; i++){
            Flight flight = problem.getFlightAt(i);
            List<Integer> validAlleles = new ArrayList<>();
            List<Integer> possibleAlleles = new ArrayList<>();
            for(int j = 0; j < problem.getScheduleGates().size(); j++) {
                FlightGate gate = problem.getFlightGateAt(j);
                if(gate.acceptFlight(flight)) {
                    possibleAlleles.add(j);
                    if(gate.isAdHocGate()) {
                        if(gate.getRule().getRuleType() == AdHocRule.IS_RUNWAY_GATE
                                && (flight.isStayOverNight() ||
                                flight.getDepartureTime() - flight.getArrivalTime() <= 3 * 3600 )) {
                            validAlleles.add(j);
                        }else if(gate.getRule().getRuleType() == AdHocRule.IS_COMPOSITE
                                && flight.getDepartureTime() - flight.getArrivalTime() <= 3 * 3600) {
                            validAlleles.add(j);
                        }
                    }
                    if(nearGateHeuristic(flight) && gate.isNearGate()) {
                        validAlleles.add(j);
                    }
                    if(farGateHeuristic(flight) && !gate.isNearGate()) {
                        validAlleles.add(j);
                    }
                }
            }
            validAlleles = validAlleles.isEmpty() ? possibleAlleles : validAlleles;
            int[] candidates = new int[validAlleles.size()];
            for(int k = 0; k < candidates.length; k++) {
                candidates[k] = validAlleles.get(k);
            }
            valids[i] = candidates;
        }
        this.genotype = new Genotype(popSize, muteRate, crossRate, valids, this::evaluate);
    }

    private boolean nearGateHeuristic(Flight info) {
        return info.getDepartureTime() - info.getArrivalTime() <= 1.5 * 60 * 60;
    }

    private boolean farGateHeuristic(Flight info) {
        return info.getDepartureTime() - info.getArrivalTime() >= 24 * 60 * 60
                || (info.getDepartureTime() - info.getArrivalTime() >= 3 * 60 * 60
                && isInDayTime(info.getArrivalTime()));
    }

    private boolean isInDayTime(int timeStamp) {
        Date date = new Date(timeStamp);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        return hour > 6 && hour <= 21;
    }

    double evaluate(int[] chromosome) {
        ScheduleSolution solution = new ScheduleSolution(problem);
        for(int i = 0; i < chromosome.length; i++) {
            Flight flight = problem.getFlightAt(i);
            FlightGate gate = problem.getFlightGateAt(chromosome[i]);
            solution.assign(flight, gate);
        }
        return ScheduleEvaluator.evaluateForGenetic(solution);
    }

    public ScheduleSolution getSolution() {
        return solution;
    }

    public void run() {
        int iter = 0;
        while(iter++ < maxIterations) {
            this.genotype.evolve();
            System.out.println("iteration = "+iter+" value = "+this.genotype.getFittestValue());
        }
        int[] chromosome = this.genotype.getBestChromosome();
        solution = new ScheduleSolution(problem);
        for(int i = 0; i < chromosome.length; i++) {
            Flight flight = problem.getFlightAt(i);
            FlightGate gate = problem.getFlightGateAt(chromosome[i]);
            solution.assign(flight, gate);
        }
    }

}
